import Tablero
import Othello
import HUnit
import Maybe

-- evaluar t para correr todos los tests
t = runTestTT allTests

allTests = test [ 
	"triviales" ~: testsTriviales,
	"tablero" ~: testsTablero,
    
    -- Ejercicio 1
    "tableroVacio" ~: testTableroVacio,
    "tableroInicial" ~: testTableroInicial,
    
    -- Ejercicio 2
    "poner" ~: testPoner,
    
    -- Ejercicio 3
    "desplazarFila" ~: testDesplazarFila,
    "desplazarColumna" ~: testDesplazarColumna,
    
    -- Ejercicio 4
    "generar" ~: testGenerar,
    
    -- Ejercicio 5
    "posicionesAInvertir" ~: testPosicionesAInvertir,
    
    -- Ejercicio 6
    "inverirTodas" ~: testInvertirTodas,
    
    -- Ejercicio 7
    "esJugadaValida" ~: testEsJugadaValida,
    "efectuarJugada" ~: testEfectuarJugada,
    "jugar" ~: testJugar,
    
    -- Ejercicio 8
    "jugadasPosibles" ~: testJugadasPosibles,

    -- Ejercicio 9
    "foldNodosVisitados" ~: testDefinicionesFold,

    -- Ejercicio 10
    "podar" ~: testPodar,
    
    -- Ejercicio 12
    "termino" ~: testTermino,
    "ganador" ~: testGanador,
    
    -- Ejercicio 13
    "valuacionOthello" ~: testValuacionOthello
	]

testsTriviales = test [
	1 ~=? 1,
	2 ~=? 2,
	[1,2] ~=? [1,2],
	[1,2] ~~? [2,1]
	]
	
testsTablero = test [
	_N ~=? fromJust (contenido d_4 tableroInicial),
	_N ~=? fromJust (contenido e_5 tableroInicial),
	_B ~=? fromJust (contenido e_4 tableroInicial),
	_B ~=? fromJust (contenido d_5 tableroInicial),
	Nothing ~=? contenido a_1 tableroInicial
	]

-- Ejercicio 1

testTableroVacio = test [
    True ~=? all isNothing (colores vacio)
    ]
    
testTableroInicial = test [
    True ~=? all (== Just _N) [ contenido p tableroInicial | p <- posicionesInicialesNegro ],
    True ~=? all (== Just _B) [ contenido p tableroInicial | p <- posicionesInicialesBlanco ],
    True ~=? all isNothing [contenido p tableroInicial | p <- posiciones,
                                                         not (p `elem` posicionesInicialesNegro ++ posicionesInicialesBlanco)]
    ]

-- Ejercicio 2

testPoner = test [
    -- pone un color en una ubicacion vacia
    Just _N ~=? contenido a_1 (poner a_1 _N tableroInicial),
    -- reemplaza un color previamente definido
    Just _B ~=? contenido d_4 (poner d_4 _B tableroInicial)
    ]
    
-- Ejercicio 3

testDesplazarFila = test [
    a_2 ~=? desplazarFila (1) a_1,
    a_1 ~=? desplazarFila (-1) a_2,
    ('a', 9) ~=? desplazarFila (1) a_8
    ]
    
testDesplazarColumna = test [
    b_1 ~=? desplazarColumna (1) a_1,
    a_1 ~=? desplazarColumna (-1) b_1,
    ('i', 1) ~=? desplazarColumna (1) h_1
    ]
    
-- Ejercicio 4

testGenerar = test [
    [f_5, f_6, f_7, f_8] ~=? generar f_5 (desplazarFila 1),
    [f_5, g_6, h_7] ~=? generar f_5 ((desplazarFila 1).(desplazarColumna 1))
    ]

-- Ejercicio 5

-- Función auxiliar para definir tableros en tests
foldPoner :: Color -> [Posicion] -> Tablero -> Tablero
foldPoner c xs t = foldr (\p rec -> poner p c rec) t xs

testPosicionesAInvertir = test [
    --    a b c d e        a b c d e
    -- 5 |B| |B| |B|    5 |B| |B| |B|
    -- 4 | |N|N|N| |    4 | |X|X|X| |
    -- 3 |B|N|B|N|B| => 3 |B|B|B|X|B|
    -- 2 | |N|N|N| |    2 | |X|X|X| |
    -- 1 |B| |B| |B|    1 |B| |B| |B|
    [b_2, b_3, b_4, c_2, c_4, d_2, d_3, d_4] ~~? posicionesAInvertir c_3 ( foldPoner _N [b_2, b_3, b_4, c_2, c_4, d_2, d_3, d_4] ( foldPoner _B [a_1, c_1, e_1, a_3, c_3, e_3, a_5, c_5, e_5] vacio ) ),
    
    --    a b c d e        a b c d e
    -- 5 | | | | | |    5 | | | | | |
    -- 4 | |N|N|N| |    4 | |N|N|N| |
    -- 3 | |N|B|N| | => 3 | |N|B|N| |
    -- 2 | |N|N|N| |    2 | |N|N|N| |
    -- 1 | | | | | |    1 | | | | | |
    [] ~~? posicionesAInvertir c_3 ( foldPoner _N [b_2, b_3, b_4, c_2, c_4, d_2, d_3, d_4] ( foldPoner _B [c_3] vacio ) ),
    
    --    a b c d e        a b c d e
    -- 5 | | | | | |    5 | | | | | |
    -- 4 | | | | | |    4 | | | | | |
    -- 3 | | |B| | | => 3 | | |B| | |
    -- 2 | |N| | | |    2 | |N| | | |
    -- 1 |N| | | | |    1 |N| | | | |
    [] ~~? posicionesAInvertir c_3 ( foldPoner _N [a_1, b_2] ( foldPoner _B [c_3] vacio ) ),
    
    --    a b c d e        a b c d e
    -- 5 | | |B| | |    5 | | |B| | |
    -- 4 | | |N| | |    4 | | |N| | |
    -- 3 | | | | | | => 3 | | | | | |
    -- 2 | | |B| | |    2 | | |B| | |
    -- 1 | | | | | |    1 | | | | | |
    [] ~~? posicionesAInvertir c_2 ( foldPoner _N [c_4] ( foldPoner _B [c_2, c_5] vacio ) )
    ]

-- Ejercicio 6

testInvertirTodas = test [
    foldPoner _B [d_4, e_5] tableroInicial ~=? invertirTodas [d_4, e_5] tableroInicial
    ]

-- Ejercicio 7

juegoInicial :: Juego
juegoInicial = J _B tableroInicial

testEsJugadaValida = test [
    -- Paso es una jugada valida
    True ~=? esJugadaValida Paso (J _B vacio),
    -- La casilla de destino no puede estar fuera del tablero
    False ~=? esJugadaValida (M a_8) (J _B vacio),
    -- La casilla de destino no puede estar vacia
    False ~=? esJugadaValida (M a_1) (J _B vacio),
    -- Se tienen que rodear fichas del jugador
    False ~=? esJugadaValida (M e_4) juegoInicial,
    True ~=? esJugadaValida (M c_4) juegoInicial
    ]
    
testEfectuarJugada = test [
    tableroInicial ~=? efectuarJugada Paso juegoInicial,
    foldPoner _B [c_4, d_4] tableroInicial ~=?efectuarJugada (M c_4) juegoInicial
    ]
    
testJugar = test [
    True ~=? isNothing  (jugar (M ('a', 8)) juegoInicial),
    Just (J _N (efectuarJugada (M c_4) juegoInicial)) ~=? jugar (M c_4) juegoInicial
    ]

-- Ejercicio 8

tableroCompleto :: Tablero
tableroCompleto = foldPoner _B posiciones vacio

juegoCompleto :: Juego
juegoCompleto = J _B tableroCompleto

tableroCasiCompleto :: Tablero
tableroCasiCompleto = poner a_1 _N (foldPoner _B [p | p <- posiciones, p /= c_3] vacio)

juegoCasiCompleto :: Juego
juegoCasiCompleto = J _N tableroCasiCompleto

testJugadasPosibles = test [
    [(Paso, J _N tableroCompleto)] ~=? jugadasPosibles juegoCompleto,
    [(M c_3, J _B (foldPoner _N [b_2, c_3] tableroCasiCompleto))] ~=? jugadasPosibles juegoCasiCompleto
    ]

-- Ejercicio 9

arbolCompleto7Nodos :: Arbol Int
arbolCompleto7Nodos =    Nodo 1 [ Nodo 2 [Nodo 3 [], Nodo 4 []],
                                   Nodo 5 [Nodo 6 [], Nodo 7 []]
                                ]

testDefinicionesFold = test [
    -- cantidad de nodos usando foldArbol
    7 ~=? foldArbol (\elemento nodosAbajo -> 1 + nodosAbajo) sum arbolCompleto7Nodos,
    -- suma de los elementos usando foldArbol
    28 ~=? foldArbol (+) sum arbolCompleto7Nodos,
    -- maximo de los elementos usando foldArbol
    7 ~=? foldArbol max (\l -> if null l then -1 else maximum l) arbolCompleto7Nodos
    ]


-- Ejercicio 10

arbolIncompleto :: Arbol Char
arbolIncompleto =   Nodo 'a' [  Nodo 'b' [],
                                Nodo 'c' [Nodo 'd' [], Nodo 'e' []]
                            ]

testPodar = test [
    Nodo 1 [] ~=? podar 1 arbolCompleto7Nodos,
    Nodo 1 [Nodo 2 [], Nodo 5 []] ~=? podar 2 arbolCompleto7Nodos,
    arbolCompleto7Nodos ~=? podar 3 arbolCompleto7Nodos,
    arbolCompleto7Nodos ~=? podar 4 arbolCompleto7Nodos,
    (Nodo 'a' [Nodo 'b' [], Nodo 'c' []]) ~=? podar 2 arbolIncompleto
    ]


-- Ejercicio 12
testTermino = test [
    False ~=? termino juegoInicial,
    True ~=? termino juegoCompleto,
    False ~=? termino juegoCasiCompleto
    ]
    
testGanador =  test [
    Nothing ~=? ganador juegoInicial,
    Nothing ~=? ganador (J _N tableroCasiCompleto),
    Nothing ~=? ganador (J _B tableroCasiCompleto),
    _B ~=? fromJust ( ganador (J _N tableroCompleto) ),
    _B ~=? fromJust ( ganador (J _B tableroCompleto) )
    ]
    
-- Ejercicio 13
testValuacionOthello = test [
    -- Juega el Blanco
   1 ~=? valuacionOthello juegoCompleto,
   -1 ~=? valuacionOthello (J Negro tableroCompleto),
   2 * (2/64) - 1 ~=? valuacionOthello juegoInicial,
   2 * (2/64) - 1 ~=? valuacionOthello (J Negro tableroInicial),
   2 * (1/64) - 1 ~=? valuacionOthello juegoCasiCompleto,
   2 * (62/64) - 1 ~=? valuacionOthello (J Blanco tableroCasiCompleto)
   ]
    
-- idem ~=? pero sin importar el orden
(~~?) :: (Ord a, Eq a, Show a) => [a] -> [a] -> Test
expected ~~? actual = (sort expected) ~=? (sort actual)
	where
		sort = foldl (\r e -> push r e) []
		push r e = (filter (e<=) r) ++ [e] ++ (filter (e>) r)

(~~) :: (Ord a, Eq a, Show a) => [a] -> [a] -> Bool
expected ~~ actual = (sort expected) == (sort actual)
	where
		sort = foldl (\r e -> push r e) []
		push r e = (filter (e<=) r) ++ [e] ++ (filter (e>) r)

-- constantes para que los tests sean más legibles		
_N = Negro
_B = Blanco
a_1 = ('a',1::Int)
b_1 = ('b',1::Int)
c_1 = ('c',1::Int)
d_1 = ('d',1::Int)
e_1 = ('e',1::Int)
f_1 = ('f',1::Int)
g_1 = ('g',1::Int)
h_1 = ('h',1::Int)

a_2 = ('a',2::Int)
b_2 = ('b',2::Int)
c_2 = ('c',2::Int)
d_2 = ('d',2::Int)
e_2 = ('e',2::Int)
f_2 = ('f',2::Int)
g_2 = ('g',2::Int)
h_2 = ('h',2::Int)

a_3 = ('a',3::Int)
b_3 = ('b',3::Int)
c_3 = ('c',3::Int)
d_3 = ('d',3::Int)
e_3 = ('e',3::Int)
f_3 = ('f',3::Int)
g_3 = ('g',3::Int)
h_3 = ('h',3::Int)

a_4 = ('a',4::Int)
b_4 = ('b',4::Int)
c_4 = ('c',4::Int)
d_4 = ('d',4::Int)
e_4 = ('e',4::Int)
f_4 = ('f',4::Int)
g_4 = ('g',4::Int)
h_4 = ('h',4::Int)

a_5 = ('a',5::Int)
b_5 = ('b',5::Int)
c_5 = ('c',5::Int)
d_5 = ('d',5::Int)
e_5 = ('e',5::Int)
f_5 = ('f',5::Int)
g_5 = ('g',5::Int)
h_5 = ('h',5::Int)

a_6 = ('a',6::Int)
b_6 = ('b',6::Int)
c_6 = ('c',6::Int)
d_6 = ('d',6::Int)
e_6 = ('e',6::Int)
f_6 = ('f',6::Int)
g_6 = ('g',6::Int)
h_6 = ('h',6::Int)

a_7 = ('a',7::Int)
b_7 = ('b',7::Int)
c_7 = ('c',7::Int)
d_7 = ('d',7::Int)
e_7 = ('e',7::Int)
f_7 = ('f',7::Int)
g_7 = ('g',7::Int)
h_7 = ('h',7::Int)

a_8 = ('a',8::Int)
b_8 = ('b',8::Int)
c_8 = ('c',8::Int)
d_8 = ('d',8::Int)
e_8 = ('e',8::Int)
f_8 = ('f',8::Int)
g_8 = ('g',8::Int)
h_8 = ('h',8::Int)
